package com.leonzhangxf.bucksdemo.order.domain;

import com.google.common.collect.Lists;
import com.leonzhangxf.bucksdemo.order.domain.value.OrderAmount;
import com.leonzhangxf.bucksdemo.order.domain.value.OrderId;
import com.leonzhangxf.bucksdemo.order.domain.value.OrderStatus;
import com.leonzhangxf.bucksdemo.order.domain.value.OrderStatusChangeLog;
import com.leonzhangxf.bucksdemo.order.domain.exception.OrderStatusNotReachableException;
import lombok.Getter;

import java.io.Serializable;
import java.time.Instant;
import java.util.Collections;
import java.util.List;


public class Order implements Serializable {

    private static final long serialVersionUID = 1L;

    private final OrderId orderId;

    /**
     * 订单应付金额
     */
    @Getter
    private OrderAmount amount;

    @Getter
    private OrderStatus status;

    private List<OrderStatusChangeLog> statusChangeLogs;

    /**
     * 初始化一个新的订单
     *
     * @param orderId 订单ID
     * @param amount  订单金额
     * @return 订单
     */
    public static Order newOrder(OrderId orderId, OrderAmount amount) {
        return new Order(orderId, amount, OrderStatus.GENERATED);
    }

    public Order(OrderId orderId, OrderAmount amount, OrderStatus status) {
        this(orderId, amount, status, null);
    }

    public Order(
        OrderId orderId, OrderAmount amount, OrderStatus status,
        List<OrderStatusChangeLog> statusChangeLogs
    ) {
        this.orderId = orderId;
        this.amount = amount;

        if (null == status) {
            this.status = OrderStatus.GENERATED;
        } else {
            this.status = status;
        }

        this.statusChangeLogs = statusChangeLogs;
    }

    public void inPaying() {
        changeStatus(OrderStatus.PAYING);
    }

    public void paid() {
        changeStatus(OrderStatus.PAID);
    }

    public void effected() {
        changeStatus(OrderStatus.EFFECTED);
    }

    public void closed() {
        changeStatus(OrderStatus.CLOSED);
    }

    public void expired() {
        changeStatus(OrderStatus.EXPIRED);
    }

    private void changeStatus(OrderStatus targetStatus) {
        OrderStatus beforeStatus = this.status;
        if (!beforeStatus.statusReachable(targetStatus)) {
            throw new OrderStatusNotReachableException(
                String.format("订单状态无法由%s变更为%s", beforeStatus, targetStatus)
            );
        }
        this.status = targetStatus;
        addStatusChangeLog(beforeStatus, targetStatus);
    }

    private void addStatusChangeLog(OrderStatus beforeStatus, OrderStatus afterStatus) {
        getStatusChangeLogs().add(new OrderStatusChangeLog(beforeStatus, afterStatus, Instant.now()));
    }

    public OrderId getOrderId() {
        return new OrderId(orderId.getId());
    }

    public List<OrderStatusChangeLog> getUnmodifiableStatusChangeLogs() {
        if (null == statusChangeLogs) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(statusChangeLogs);
    }

    private List<OrderStatusChangeLog> getStatusChangeLogs() {
        if (null == statusChangeLogs) {
            statusChangeLogs = Lists.newArrayList();
        }
        return statusChangeLogs;
    }

    @Override
    public String toString() {
        return "Order{" +
            "orderId=" + orderId +
            ", amount=" + amount +
            ", status=" + status +
            ", statusChangeLogs=" + statusChangeLogs +
            '}';
    }
}
